package com.spring.service.impl;


import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;


import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.spring.dataobject.LeaveBill;
import com.spring.enums.BillStatusEnum;
import com.spring.form.WorkflowForm;
import com.spring.service.LeaveBillService;
import com.spring.service.WorkflowService;



/**
 * 创建时间：2018年6月22日 上午11:46:56 
 * 项目名称：activiti-spring
 * 
 * @author Mo Xiaobao
 * @version 1.0
 * @since JDK 1.8 
 * 文件名称：WorkflowServiceImpl.java 
 * 类说明：
 */
@Service
public class WorkflowServiceImpl implements WorkflowService {

	@Autowired
	private RepositoryService repositoryService;

	@Autowired
	private RuntimeService runtimeService;

	@Autowired
	private TaskService taskService;

	@Autowired
	private FormService formService;

	@Autowired
	private HistoryService historyService;
	
	@Autowired
	private LeaveBillService leaveBillService;

	/** 部署流程定义 */
	@Override
	public void saveNewDeploye(File file, String filename) {
		try {
			ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(file));
			repositoryService.createDeployment()// 创建部署对象
					.addZipInputStream(zipInputStream).name(filename)// 添加部署名称
					.deploy();// 完成部署
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/** 查询部署对象信息，对应表（act_re_deployment） */
	@Override
	public List<Deployment> findDeploymentList() {
		List<Deployment> depList = repositoryService.createDeploymentQuery()
													.orderByDeploymenTime().desc()
													.list();
		return depList;
	}

	/**查询流程定义的信息，对应表（act_re_procdef）*/
	@Override
	public List<ProcessDefinition> findProcessDefinitionList() {
		
		List<ProcessDefinition> pdList = repositoryService.createProcessDefinitionQuery()
														  .orderByProcessDefinitionVersion().desc()
														  .list();
		return pdList;
	}

	/**使用部署对象ID，删除流程定义*/
	@Override
	public void deleteProcessDefinitionByDeploymentId(String deploymentId) {
		repositoryService.deleteDeployment(deploymentId, true);
	}

	/**使用部署对象ID和资源图片名称，获取图片的输入流*/
	@Override
	public InputStream findImageInputStream(String deploymentId, String imageName) {
		return repositoryService.getResourceAsStream(deploymentId, imageName);
	}

	/**更新请假状态，启动流程实例，让启动的流程实例关联业务*/
	@Override
	public void startProcess(Long id,String username) {
		//1：获取请假单ID，使用请假单ID，查询请假单的对象LeaveBill
		LeaveBill leaveBill = leaveBillService.findLeaveBillById(id);
		//2：更新请假单的请假状态从0变成1（初始录入-->审核中）
		leaveBill.setStatus(BillStatusEnum.GOING.getCode());
		leaveBillService.saveLeaveBill(leaveBill);
		//3：使用当前对象获取到流程定义的key（对象的名称就是流程定义的key）
		String key = leaveBill.getClass().getSimpleName();
		String objId = key + "." + id;
		Map<String, Object> variables = new HashMap<String, Object>();
		variables.put("inputUser",username);
		variables.put("objId", objId);
		runtimeService.startProcessInstanceByKey(key,objId,variables);
	}

	/** 使用当前用户名查询正在执行的任务表，获取当前任务的集合List<Task>*/
	@Override
	public List<Task> findTaskListByName(String name) {
		List<Task> list = taskService.createTaskQuery().taskAssignee(name)
									.orderByTaskCreateTime()
									.desc()
									.list();
		return list;
	}

	/**使用任务ID，获取当前任务节点中对应的Form key中的连接的值*/
	@Override
	public String findTaskFormKeyByTaskId(String taskId) {
		TaskFormData taskFormData = formService.getTaskFormData(taskId);
		//获取Form key的值
		String url = taskFormData.getFormKey();
		return url;
	}

	/**一：使用任务ID，查找请假单ID，从而获取请假单信息*/
	@Override
	public LeaveBill findLeaveBillByTaskId(String taskId) {
		
		//1：使用任务ID，查询任务对象Task
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		//2：使用任务对象Task获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//3：使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
														.processInstanceId(processInstanceId)
														.singleResult();
		//4：使用流程实例对象获取BUSINESS_KEY
		String buniness_key = processInstance.getBusinessKey();
		//5：获取BUSINESS_KEY对应的主键ID，使用主键ID，查询请假单对象（LeaveBill.1）
		String billId = null ;
		if (!StringUtils.isEmpty(buniness_key)) {
			billId = buniness_key.split("\\.")[1];
		}
		//查询请假单对象
		LeaveBill leaveBill = leaveBillService.findLeaveBillById(Long.parseLong(billId));
		return leaveBill;
	}

	/**已知任务ID，查询ProcessDefinitionEntiy对象，从而获取当前任务完成之后的连线名称，并放置到List<String>集合中*/
	@Override
	public List<String> findOutComeListByTaskId(String taskId) {
		List<String> list = new ArrayList<String>();
		//1:使用任务ID，查询任务对象
		Task task = taskService.createTaskQuery()
							   .taskId(taskId)
							   .singleResult();
		//2：获取流程定义ID
		String processDefinitionId = task.getProcessDefinitionId();
		//3：查询ProcessDefinitionEntiy对象
		ProcessDefinitionEntity processDefinitionEntity =
		(ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
		//使用任务对象Task获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
														.processInstanceId(processInstanceId)
														.singleResult();
		//获取当前活动的id
		String activityId = processInstance.getActivityId();
		//4：获取当前的活动
		ActivityImpl activityImpl =
		processDefinitionEntity.findActivity(activityId);
		//5：获取当前活动完成之后连线的名称
		List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
		if (pvmList.size()>0 && pvmList!=null) {
			for (PvmTransition pvmTransition : pvmList) {
				String name = (String) pvmTransition.getProperty("name");
				if (!StringUtils.isEmpty(name)) {
					list.add(name);
				}else {
					list.add("默认提交");
				}
			}
		}
		return list;
	}
	
	/**获取批注信息，传递的是当前任务ID，获取历史任务ID对应的批注*/
	@Override
	public List<Comment> findCommentByTaskId(String taskId) {
		
		//使用当前任务ID，获取当前任务对象
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		//获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		
		List<Comment> commentList =taskService.getProcessInstanceComments(processInstanceId);
		
		return commentList;
	}

	/**指定连线的名称完成任务*/
	@Override
	public void saveSubmitTask(WorkflowForm workflowForm,String username) {
		//获取任务ID
		String taskId = workflowForm.getTaskId();
		//获取连线的名称
		String outcome = workflowForm.getOutcome();
		//批注信息
		String message = workflowForm.getComment();
		//获取请假单ID
		Long id = workflowForm.getId();
		
		/**
		 * 1：在完成之前，添加一个批注信息，向act_hi_comment表中添加数据，用于记录对当前申请人的一些审核信息
		 */
		//使用任务ID，查询任务对象，获取流程流程实例ID
		Task task = taskService.createTaskQuery()
								.taskId(taskId)
								.singleResult();
		//获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		/**
		 * 注意：添加批注的时候，由于Activiti底层代码是使用：
		 * 		String userId = Authentication.getAuthenticatedUserId();
			    CommentEntity comment = new CommentEntity();
			    comment.setUserId(userId);
			  所有需要从Session中获取当前登录人，作为该任务的办理人（审核人），对应act_hi_comment表中的User_ID的字段，不过不添加审核人，该字段为null
			 所以要求，添加配置执行使用Authentication.setAuthenticatedUserId();添加当前任务的审核人
		 * */
		
		
		Authentication.setAuthenticatedUserId(username);
		taskService.addComment(taskId, processInstanceId, message);
		/**
		 * 2：如果连线的名称是“默认提交”，那么就不需要设置，如果不是，就需要设置流程变量
		 * 在完成任务之前，设置流程变量，按照连线的名称，去完成任务
				 流程变量的名称：outcome
				 流程变量的值：连线的名称
		 */
		Map<String, Object> variables = new HashMap<String, Object>();
		if (outcome!= null && !outcome.equals("默认提交")) {
			variables.put("outcome", outcome);
		}
		
		//3：使用任务ID，完成当前人的个人任务，同时流程变量
		taskService.complete(taskId, variables);
		//4：当任务完成之后，需要指定下一个任务的办理人（使用类）-----已经开发完成
		/**
		 * 5：在完成任务之后，判断流程是否结束
   			如果流程结束了，更新请假单表的状态从1变成2（审核中-->审核完成）
		 */
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()
											.processInstanceId(processInstanceId)
											.singleResult();
		//流程结束了
		if (pi == null) {
			LeaveBill leaveBill = leaveBillService.findLeaveBillById(id);
			leaveBill.setStatus(BillStatusEnum.FINISHED.getCode());
			leaveBillService.saveLeaveBill(leaveBill);
		}
	}

	
	/**获取任务ID，获取任务对象，使用任务对象获取流程定义ID，查询流程定义对象*/
	@Override
	public ProcessDefinition findProcessDefinitionByTaskId(String taskId) {
		//使用任务ID，查询任务对象
		Task task = taskService.createTaskQuery()
								.taskId(taskId)
								.singleResult();
		//获取流程定义ID
		String processDefinitionId = task.getProcessDefinitionId();
		//查询流程定义的对象
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()//创建流程定义查询对象，对应表act_re_procdef 
					.processDefinitionId(processDefinitionId)//使用流程定义ID查询
					.singleResult();
		return pd;
	}

	
	/**
	 * 查看当前活动，获取当期活动对应的坐标x,y,width,height，将4个值存放到Map<String,Object>中
	   map集合的key：表示坐标x,y,width,height
	   map集合的value：表示坐标对应的值
	 */
	@Override
	public Map<String, Object> findCoordingByTask(String taskId) {
		//存放坐标
		Map<String, Object> map = new HashMap<String,Object>();
		//使用任务ID，查询任务对象
		Task task = taskService.createTaskQuery()//
					.taskId(taskId)//使用任务ID查询
					.singleResult();
		//获取流程定义的ID
		String processDefinitionId = task.getProcessDefinitionId();
		//获取流程定义的实体对象（对应.bpmn文件中的数据）
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processDefinitionId);
		//流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程实例ID，查询正在执行的执行对象表，获取当前活动对应的流程实例对象
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//创建流程实例查询
					.processInstanceId(processInstanceId)//使用流程实例ID查询
					.singleResult();
		//获取当前活动的ID
		String activityId = pi.getActivityId();
		ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);
		map.put("x", activityImpl.getX());
		map.put("y", activityImpl.getY());
		map.put("width", activityImpl.getWidth());
		map.put("height", activityImpl.getHeight());
		return map;
	}

	/**使用请假单ID，查询历史批注信息*/
	@Override
	public List<Comment> findCommentByLeaveBillId(Long billId) {
		//使用请假单ID，查询请假单对象
		LeaveBill leaveBill = leaveBillService.findLeaveBillById(billId);
		//获取对象的名称
		String objectName = leaveBill.getClass().getSimpleName();
		
		String objId = objectName +"."+ billId;
		/**使用历史的流程变量查询，返回历史的流程变量的对象，获取流程实例ID*/
		HistoricVariableInstance hInstance = historyService.createHistoricVariableInstanceQuery()//对应历史的流程变量表
											.variableValueEquals("objId", objId)//使用流程变量的名称和流程变量的值查询
											.singleResult();
		String processInstanceId = hInstance.getProcessInstanceId();
		List<Comment> list = taskService.getProcessInstanceComments(processInstanceId);
		
		return list;
	}

	

	
	
}
